package cn.iocoder.yudao.module.engine.service.core.timer.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.cache.NettyChannelCacheService;
import cn.iocoder.yudao.framework.common.util.id.IdUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.module.engine.dal.redis.engine.EngineCacheRedisDAO;
import cn.iocoder.yudao.module.engine.enums.EnumAudioType;
import cn.iocoder.yudao.module.engine.model.config.RemindConfig;
import cn.iocoder.yudao.module.engine.model.context.EngineSessionContext;
import cn.iocoder.yudao.module.engine.model.context.EngineTimerStatusCacheContext;
import cn.iocoder.yudao.module.engine.model.engine.EngineOutputData;
import cn.iocoder.yudao.module.engine.service.core.queue.audio.AudioQueueManager;
import cn.iocoder.yudao.module.engine.service.core.timer.IEngineTimerService;
import cn.iocoder.yudao.module.engine.service.log.IEngineLogService;
import cn.iocoder.yudao.module.engine.util.AudioUtils;
import cn.iocoder.yudao.module.engine.util.EngineConvertUtils;
import cn.iocoder.yudao.module.infra.api.config.IConfigApi;
import cn.iocoder.yudao.module.infra.util.GameMatchIdUtil;
import cn.iocoder.yudao.module.infra.util.TraceIdUtil;
import cn.iocoder.yudao.module.resource.api.IAudioServiceApi;
import cn.iocoder.yudao.module.resource.api.enums.MessageSourceTypeEnum;
import cn.iocoder.yudao.module.resource.api.model.AudioDTO;
import io.netty.util.HashedWheelTimer;
import io.netty.util.Timeout;
import io.netty.util.TimerTask;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

@Service("engine-timer-guide")
@Slf4j
public class GuideTimerServiceImpl implements IEngineTimerService {
    private static final String CONFIG_KEY_REMIND = "remind.config";

    @Resource
    private IAudioServiceApi audioApi;
    @Resource
    private AudioQueueManager audioQueueManager;
    @Resource
    private EngineCacheRedisDAO engineCacheRedisDAO;
    @Resource
    private IEngineLogService logService;
    @Resource
    private IConfigApi configApi;
    @Resource
    private NettyChannelCacheService channelCacheService;

    @Override
    public void addTimerTask(HashedWheelTimer wheelTimer, EngineSessionContext sessionContext, ExecutorService timerExecutors) {
        final String remindConfigString = configApi.getValueByKey(CONFIG_KEY_REMIND);
        final RemindConfig remindConfig = JsonUtils.parseObject(remindConfigString, RemindConfig.class);
        for (RemindConfig.Timer timer : remindConfig.getTimers()) {
            log.info("添加语音引导定时任务, userId = {}, sessionId = {}, timerId = {}",
                    sessionContext.getUserId(), sessionContext.getWsSessionId(), timer.getId());
            final Timeout timeout = wheelTimer.newTimeout(
                    new DefaultTimerTask(sessionContext, timer, timerExecutors),
                    timer.getDelaySecond(), TimeUnit.SECONDS
            );
            sessionContext.addRemindTimeout(timeout);
        }
    }

    private class DefaultTimerTask implements TimerTask {
        private final EngineSessionContext sessionContext;
        private final Long userId;
        private final RemindConfig.Timer timerConfig;
        private final ExecutorService timerExecutors;

        public DefaultTimerTask(EngineSessionContext sessionContext, RemindConfig.Timer timerConfig, ExecutorService timerExecutors) {
            this.sessionContext = sessionContext;
            this.userId = sessionContext.getUserId();
            this.timerConfig = timerConfig;
            this.timerExecutors = timerExecutors;
        }

        @Override
        public void run(Timeout timeout) throws Exception {
            timerExecutors.execute(() -> {
                executeTask(timeout);
            });
        }

        private void executeTask(Timeout timeout) {
            try {
                TraceIdUtil.setUserId(sessionContext.getUserId());
                TraceIdUtil.setTraceId(IdUtils.generatorId());
                GameMatchIdUtil.setMatchId(sessionContext.getGameMatchId());

                if (timeout.isCancelled()) {
                    log.info("语音引导定时任务-已取消, userId = {}, sessionId = {}, timerId = {}",
                            userId, sessionContext.getWsSessionId(), timerConfig.getId());
                    return;
                }

                final String sessionId = sessionContext.getWsSessionId();
                if (!channelCacheService.isOnline(sessionId)) {
                    log.warn("语音引导定时任务-用户已离线, userId = {}, sessionId = {}",
                            sessionContext.getUserId(), sessionId);
                    return;
                }

                final EngineTimerStatusCacheContext timerStatus = engineCacheRedisDAO.getEngineTimerStatusCache(sessionContext.getGameMatchId(), engineCacheRedisDAO);
                final Integer triggerCount = timerStatus.getTriggerCount(timerConfig.getId());
                final Integer maxTriggerCount = timerConfig.getMaxTriggerCount();
                if (triggerCount >= maxTriggerCount) {
                    log.info("语音引导定时任务-触发次数超限, userId = {}, sessionId = {}, timerId = {}, triggerCount = {}, maxTriggerCount = {}",
                            userId, sessionContext.getWsSessionId(), timerConfig.getId(), triggerCount, maxTriggerCount);
                    return;
                }

                // 触发次数 + 1
                timerStatus.addTriggerCount(timerConfig.getId()).syn();

                final List<String> tags = (triggerCount == 0 && CollUtil.isNotEmpty(timerConfig.getFirstTags()))
                        ? timerConfig.getFirstTags() : timerConfig.getDefaultTags();
                log.info("语音引导定时任务-触发task, userId = {}, sessionId = {}, timerId = {}, triggerCount = {}, tags = {}",
                        userId, sessionContext.getWsSessionId(), timerConfig.getId(), triggerCount, tags);

                if (CollUtil.isEmpty(tags)) {
                    log.warn("语音引导定时任务-tag为空, userId = {}, sessionId = {}, timerId = {}, tags = {}, timer = {}",
                            userId, sessionContext.getWsSessionId(), timerConfig.getId(), tags, timerConfig);
                    return;
                }

                final Integer roleId = sessionContext.getRoleId();
                final List<AudioDTO> audios = audioApi.queryAudioByTag(roleId, tags);
                log.info("查询语音引导音频, userId = {}, sessionId = {}, timerId = {}, audios.size = {}",
                        userId, sessionContext.getWsSessionId(), timerConfig.getId(), audios.size());

                final AudioDTO audio = AudioUtils.randomAudio(sessionContext, audios);
                final EngineOutputData.Audio remindAudio = EngineConvertUtils.toEngineOutputData(
                        null, IdUtil.objectId(), MessageSourceTypeEnum.SYSTEM, EnumAudioType.GUID, audio
                );
                log.info("随机语音引导音频, userId = {}, sessionId = {}, timerId = {}, remindAudio = {}",
                        userId, sessionContext.getWsSessionId(), timerConfig.getId(), JsonUtils.toJsonString(remindAudio));

                // 给client发送提醒音频
                if (remindAudio != null) {
                    logService.log(sessionContext, StrUtil.format("【输出语音引导音频{}秒】[标签]: {}, [内容]: {}",
                            timerConfig.getDelaySecond(), tags, remindAudio.getAudioDesc()));
                    audioQueueManager.addAndSendAudioNotSave(sessionContext, remindAudio);
                } else {
                    logService.log(sessionContext, StrUtil.format("【无语音引导音频{}秒】[标签]: {}", timerConfig.getDelaySecond(), tags));
                }

                // 重新添加定时任务
                //reAddRemindTask(timeout);
            } catch (Exception e) {
                log.warn(StrUtil.format("语音引导定时任务-异常, userId = {}, sessionId = {}, timerId = {}",
                        userId, sessionContext.getWsSessionId(), timerConfig.getId()), e);
            } finally {
                sessionContext.removeRemindTimeout(timeout);
                TraceIdUtil.removeUserId();
                TraceIdUtil.removeTraceId();
                GameMatchIdUtil.removeMatchId();
            }
        }

//        // 重新添加定时任务
//        private void reAddRemindTask(Timeout timeout) {
//            final Timeout newTimeout = wheelTimer.newTimeout(
//                    new DefaultTimerTask(sessionContext, timerConfig),
//                    timerConfig.getDelaySecond(), TimeUnit.SECONDS
//            );
//            sessionContext.addRemindTimeout(newTimeout);
//            log.info("重新添加定时任务, userId = {}, sessionId = {}, timerId = {}, timeout = {}",
//                    userId, sessionContext.getWsSessionId(), timerConfig.getId(), timeout);
//        }
    }

    @Override
    public void cancelTimerTask(HashedWheelTimer wheelTimer, EngineSessionContext sessionContext, ExecutorService timerExecutors) {
        final Collection<Timeout> remindTimeoutList = sessionContext.getRemindTimeoutList();
        if (CollUtil.isNotEmpty(remindTimeoutList)) {
            for (Timeout timeout : remindTimeoutList) {
                if (!timeout.isCancelled()) {
                    timeout.cancel();
                    log.info("取消语音引导定时任务, userId = {}, sessionId = {}, timeout = {}",
                            sessionContext.getUserId(), sessionContext.getWsSessionId(), timeout);
                } else {
                    log.info("已取消语音引导定时任务, userId = {}, sessionId = {}, timeout = {}",
                            sessionContext.getUserId(), sessionContext.getWsSessionId(), timeout);
                }
            }

            sessionContext.clearRemindTimeout();
            log.info("语音引导定时任务-取消所有task, userId = {}, sessionId = {}",
                    sessionContext.getUserId(), sessionContext.getWsSessionId());
        }
    }

}
